package com.lapis17.stock.service.impl;

import com.google.common.collect.Lists;
import com.lapis17.stock.config.StockTaskRunable;
import com.lapis17.stock.face.StockCacheFace;
import com.lapis17.stock.mapper.StockBusinessMapper;
import com.lapis17.stock.mapper.StockMarketIndexInfoMapper;
import com.lapis17.stock.mapper.StockRtInfoMapper;
import com.lapis17.stock.pojo.entity.StockMarketIndexInfo;
import com.lapis17.stock.pojo.entity.StockRtInfo;
import com.lapis17.stock.pojo.vo.StockInfoConfig;
import com.lapis17.stock.service.StockTimerTaskService;
import com.lapis17.stock.utils.DateTimeUtil;
import com.lapis17.stock.utils.IdWorker;
import com.lapis17.stock.utils.ParseType;
import com.lapis17.stock.utils.ParserStockInfoUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {

    @Autowired
    private StockInfoConfig stockInfoConfig;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Autowired
    private StockBusinessMapper stockBusinessMapper;
    @Autowired
    private ParserStockInfoUtil parserStockInfoUtil;

    //必须保证该对象无状态
    private HttpEntity<Object> httpEntity;
    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private StockCacheFace stockCacheFace;

    /**
     * 获取国内大盘的实时数据信息
     */
    @Override
    public void getInnerMarketInfo() {
        //1.采用原始数据
        String url = stockInfoConfig.getMarketUrl() + String.join(",", stockInfoConfig.getInner());
//        //2.1 组装请求头
//        HttpHeaders headers = new HttpHeaders();
//        //必须填写，否则数据采集不到
//        headers.add("Referer", "https://finance.sina.com.cn/stock/");
//        headers.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
//        //2.2 组装请求对象
//        HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
        //2.3 resetTemplate发起请求
        String resString = restTemplate.postForObject(url, httpEntity, String.class);
        //2.java正则解析原始数据
        //3.解析数据封装entity
        String reg = "var hq_str_(.+)=\"(.+)\";";
        //编译表达式,获取编译对象
        Pattern pattern = Pattern.compile(reg);
        //匹配字符串
        Matcher matcher = pattern.matcher(resString);
        ArrayList<StockMarketIndexInfo> list = new ArrayList<>();
        //判断是否有匹配的数值
        while (matcher.find()) {
            //获取大盘的code
            String marketCode = matcher.group(1);
            //获取其它信息，字符串以逗号间隔
            String otherInfo = matcher.group(2);
            //以逗号切割字符串，形成数组
            String[] splitArr = otherInfo.split(",");
            //大盘名称
            String marketName = splitArr[0];
            //获取当前大盘的开盘点数
            BigDecimal openPoint = new BigDecimal(splitArr[1]);
            //前收盘点
            BigDecimal preClosePoint = new BigDecimal(splitArr[2]);
            //获取大盘的当前点数
            BigDecimal curPoint = new BigDecimal(splitArr[3]);
            //获取大盘最高点
            BigDecimal maxPoint = new BigDecimal(splitArr[4]);
            //获取大盘的最低点
            BigDecimal minPoint = new BigDecimal(splitArr[5]);
            //获取成交量
            Long tradeAmt = Long.valueOf(splitArr[8]);
            //获取成交金额
            BigDecimal tradeVol = new BigDecimal(splitArr[9]);
            //时间
            Date curTime = DateTimeUtil.getDateTimeWithoutSecond(splitArr[30] + " " + splitArr[31]).toDate();
            //组装entity对象
            StockMarketIndexInfo info = StockMarketIndexInfo.builder()
                    .id(idWorker.nextId())
                    .marketCode(marketCode)
                    .marketName(marketName)
                    .curPoint(curPoint)
                    .openPoint(openPoint)
                    .preClosePoint(preClosePoint)
                    .maxPoint(maxPoint)
                    .minPoint(minPoint)
                    .tradeVolume(tradeVol)
                    .tradeAmount(tradeAmt)
                    .curTime(curTime)
                    .build();
            //收集封装的对象，方便批量插入
            list.add(info);
        }
        log.info("采集的当前大盘数据：{}", list);
        //批量插入
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
//        //TODO 后续完成批量插入功能
        //4.调用matibats
        int count = stockMarketIndexInfoMapper.insertBatch(list);
        if (count > 0) {
            //通知后台终端刷新本地缓存，发送的日期数据是告知对方当前更新的股票数据所在时间点
            //发送日期对象，接收方通过接受的日期和当前日期作对比，能判断出数据延迟的时长，用于运维通知处理
            rabbitTemplate.convertAndSend("stockExchange","inner.market",new Date());
            log.info("当前时间：{}，插入大盘数据：{}成功", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),list);
        } else {
            log.info("当前时间：{}，插入大盘数据：{}失败", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),list);
        }
    }
    /**
     * 定义获取分钟级股票数据
     */
    @Override
    public void getStockRtIndex() {
        //        List<String> StockCodesList = stockBusinessMapper.getAllStockCode();
//        StockCodesList = StockCodesList.stream().map(code -> code.startsWith("6") ? "sh" + code : "sz" + code).collect(Collectors.toList());
        List<String> StockCodesList=stockCacheFace.getAllStockCodeWithPredix();
        /*Lists.partition(StockCodesList, 15).forEach(strings -> {
            String url = stockInfoConfig.getMarketUrl() + String.join(",", strings);
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
            int statusCodeValue = responseEntity.getStatusCodeValue();
            if (statusCodeValue != 200) {
                log.error("采集个股数据失败,状态码：{}", statusCodeValue);
            }
            String jsData = responseEntity.getBody();
            List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
            int count = stockRtInfoMapper.insertBatch(list);
            if (count > 0) {
                log.info("插入个股数据成功！批量插入了：{}条数据", count);
            } else {
                log.info("插入个股数据失败！");
            }
        });*/
        // 采用线程池
        /*Lists.partition(StockCodesList, 15).forEach(strings -> {
            threadPoolTaskExecutor.execute(()->{
                String url = stockInfoConfig.getMarketUrl() + String.join(",", strings);
                ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
                int statusCodeValue = responseEntity.getStatusCodeValue();
                if (statusCodeValue != 200) {
                    log.error("采集个股数据失败,状态码：{}", statusCodeValue);
                }
                String jsData = responseEntity.getBody();
                List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
                int count = stockRtInfoMapper.insertBatch(list);
                if (count > 0) {
                    log.info("插入个股数据成功！批量插入了：{}条数据", count);
                } else {
                    log.info("插入个股数据失败！");
                }
            });
        });*/
        Lists.partition(StockCodesList,15).forEach(strings->{
            Map<String, Object> taskMap = new HashMap<>();
            taskMap.put("data",strings);
            threadPoolTaskExecutor.execute(new StockTaskRunable(taskMap) {
                @Override
                public void run() {
                    String url = stockInfoConfig.getMarketUrl() + String.join(",", strings);
                    ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
                    int statusCodeValue = responseEntity.getStatusCodeValue();
                    if (statusCodeValue != 200) {
                        log.error("采集个股数据失败,状态码：{}", statusCodeValue);
                    }
                    String jsData = responseEntity.getBody();
                    List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
                    int count = stockRtInfoMapper.insertBatch(list);
                    if (count > 0) {
                        log.info("插入个股数据成功！批量插入了：{}条数据", count);
                    } else {
                        log.info("插入个股数据失败！");
                    }
                }
            });
        });

    }

    @PostConstruct
    public void initData() {
        //2.1 组装请求头
        HttpHeaders headers = new HttpHeaders();
        //必须填写，否则数据采集不到
        headers.add("Referer", "https://finance.sina.com.cn/stock/");
        headers.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
        //2.2 组装请求对象
        httpEntity = new HttpEntity<>(headers);
    }
}
